# -*- coding: utf-8 -*-
#!/usr/bin/env python

import logging
import time
import random
import urllib
import json
import operator
import copy

from datetime import datetime, timedelta
from google.appengine.api import mail
from google.appengine.api import memcache
from google.appengine.ext import ndb
from gaesessions import get_current_session
from google.appengine.api import urlfetch
from operator import itemgetter
from google.appengine.runtime import DeadlineExceededError
from google.appengine.ext.deferred import defer

import statistics as StatUtil
from models import *
from app_global import *
from events import *

def incrementStat(keyname, count):
	StatUtil.increment(keyname, count)

def incrementStats(platform, rewards):
	logging.info("rewards = %s" % rewards)
	for i in range(len(rewards)):
		if rewards[i]>0:
			StatUtil.increment(STAT_REWARDS.format(platform, i), rewards[i])

def getNotices():
	objects=[]

	for obj in Notice.query(Notice.visible==True).fetch():
		notice = {
			"id":obj.key.id(),
			"url":obj.url,
			"updated_at":obj.updated_at
		}
		objects.append(notice)
	objects = sorted(objects, key=itemgetter('updated_at'), reverse=False) 
	for obj in objects:
		del obj["updated_at"]
	return objects

def update_bout(user):
	if user.bout >= MAX_FEED_BOUT:
		return

	while True:
		if math.ceil((datetime.now() - user.bout_feed_at).total_seconds()) > INTERVAL_FEED_BOUT:
			user.bout += 1
			if user.bout == MAX_FEED_BOUT:
				user.bout_feed_at = datetime.now()
				break
			user.bout_feed_at += timedelta(seconds=INTERVAL_FEED_BOUT)
		else:
			break

def update_push_token(user):
	pushToken = UpdatedPushToken.get_by_id(user.key.id())
	
	if user.push_token is None:
		if pushToken:
			pushToken.invalid = True
			pushToken.put()
		return
	
	created = False
	if pushToken is None:
		pushToken = UpdatedPushToken(id=user.key.id())
		created = True
	if created or pushToken.invalid==True or pushToken.platform_id!=user.platform_id or pushToken.push_token!=user.push_token or pushToken.application_version!=user.application_version or pushToken.asset_version!=user.asset_version:
		pushToken.platform_id=user.platform_id
		pushToken.push_token=user.push_token
		pushToken.application_version=user.application_version
		pushToken.asset_version=user.asset_version
		pushToken.invalid=False
		pushToken.put()

def update_mission_record(user, mission):
	dirty = False
	if user.mission_record.daily_season != mission.daily_season:
		user.mission_record.initializeDaily(mission.daily_season)
		dirty = True
	if user.mission_record.weekly_season != mission.weekly_season:
		user.mission_record.initializeWeekly(mission.weekly_season)
		dirty = True
	return dirty

def getUserFriends(user, friendUsers, friendIds, season, prev=False):
	friends=[]
	now = datetime.now()
	index = 0
	
	existCount = 0
	for friendUser in friendUsers:
		if friendUser is None:
			friend = {
				"status":1,
				"id":friendIds[index],
				"score":0,
			}
		else:
			if friendUser.key.id()==user.key.id():
				friendUser = user
			else:
				existCount += 1
			if prev:
				seasonData = friendUser.prevSeasonData(season)
			else:
				seasonData = friendUser.seasonData(season)
			friend = {
				"status":0,
				"id":friendIds[index],
				"name":friendUser.name,
				"imageUrl":friendUser.image_url,
				"score":sum(seasonData.scores) if seasonData else 0,
				"scores":[int(s) for s in seasonData.scores] if seasonData else ([0]*GAME_COUNT),
				"boutBlocked":friendUser.bout_blocked,
				"pushBlocked": friendUser.push_blocked,
			}
			ufb = UserFriendBout.query(UserFriendBout.user_id==user.key.id(), UserFriendBout.friend_id==friendUser.key.id()).get()
			if ufb is None:
				friend["boutAcceptableInSec"] = 0
				friend["boutRequestableInSec"] = 0
			else:
				friend["boutAcceptableInSec"] = ufb.boutAcceptableInSec(now)
				friend["boutRequestableInSec"] = ufb.boutRequestableInSec(now)
		friends.append(friend)
		index += 1
			
	friends = sorted(friends, key=itemgetter('score'), reverse=True)
	rank = 1
	for u in friends:
		if u["id"]==user.key.id():
			break
		rank += 1
	return friends, rank, existCount

def getUserMissions(user, todayMission):
	dms = getStorageData("daily-missions")
	wms = getStorageData("weekly-missions")
	achievements = getStorageData("achievements")
	
	d3 = []
	w3 = []
	
	for m in todayMission.daily_missions:
		status = user.mission_record.daily_statuses[len(d3)]
		if status==1:
			user.mission_record.daily_statuses[len(d3)] = 0 # 양수로 바꿔서 확인 상태로 만든다.
		d3.append({
			"id":m,
			"name":dms[m-1]["name"],
			"reward":dms[m-1]["reward"],
			"amount":dms[m-1]["amount"],
			"status":status,
		})
	for m in todayMission.weekly_missions:
		status = user.mission_record.weekly_statuses[len(w3)]
		if status==1:
			user.mission_record.weekly_statuses[len(w3)] = 0 # 양수로 바꿔서 확인 상태로 만든다.
		w3.append({
			"id":m,
			"name":wms[m-1]["name"],
			"reward":wms[m-1]["reward"],
			"amount":wms[m-1]["amount"],
			"status":user.mission_record.weekly_statuses[len(w3)],
		})
	
	achievements = copy.deepcopy(achievements)
	for a in achievements:
		del a["kind"], a["condition"]
		status = -1 # 미달성, achieve_rewards에 있으면 완료이고, 없으면 미완료
		for i in range(len(user.accrue_record.achieve_rewards)):
			if a["id"]==abs(user.accrue_record.achieve_rewards[i]):
				if user.accrue_record.achieve_rewards[i]<0:
					status = 1 # 달성(미확인)
					user.accrue_record.achieve_rewards[i] = a["id"] # 양수로 바꿔서 확인 상태로 만든다.
				else:
					status = 0 # 달성(확인)
				break
		a["status"] = status
	
	return d3, w3, achievements
		
def dailyLoginBonus(user, season):
	BONUSTYPES = [None, REWARD_COIN, REWARD_COIN, REWARD_COIN, REWARD_COIN, REWARD_COIN, REWARD_COIN, REWARD_CASH]
	BONUSAMOUNTS = [0, 200, 500, 750, 1000, 1250, 1500, 1]
		
	now = datetime.now()
	bonusType = None
	bonusAmount = 0
	
	if user.loggedin_at is None or (int((replace_timezone(now, 'Asia/Seoul').strftime('%Y%m%d'))) != int(replace_timezone(user.loggedin_at, 'Asia/Seoul').strftime('%Y%m%d'))):
		if user.daily_stamp>=7:
			user.daily_stamp = 0
		user.daily_stamp += 1
		bonusType = BONUSTYPES[user.daily_stamp]
		bonusAmount = BONUSAMOUNTS[user.daily_stamp]

	user.loggedin_at = now
	return bonusType, bonusAmount

def obtainReward(user, reward, rewardArg, appendStat=False):
	if reward==REWARD_CASH:
		user.cash += rewardArg
	elif reward==REWARD_COIN:
		user.coin += rewardArg
	elif reward==REWARD_BOUT:
		user.bout += rewardArg
	elif reward==REWARD_CHARACTER:
		characterKey = user.createCharacter(rewardArg)
		if characterKey:
			user.character_keys.append(characterKey)
		else: # 대체품으로 지급해야 함
			logging.info("already exists characters")
			user.coin += 4000
			reward = REWARD_COIN
			rewardArg = 4000
	
	# 통계에 보상항목들 추가
	if appendStat:
		defer(incrementStat, STAT_REWARDS.format(user.platform_id, reward), rewardArg if reward!=REWARD_CHARACTER else 1)
	return reward, rewardArg


def checkAchievement(user, kind, withObtain=False):
	achievements = getStorageData('achievements')
	completedAchievements = []
	for a in achievements:
		found = False
		for ar in user.accrue_record.achieve_rewards:
			if a["id"]==abs(ar):
				found = True
				break
		if not found: # 아직 보상을 준적이 없는 것들 중에서 
			if a["kind"]==kind and eval(a["condition"]): # 조건을 만족하면
				completedAchievements.append({"name":a["name"], "reward":a["reward"], "amount":a["amount"]})
				user.accrue_record.achieve_rewards.append(-a["id"]) # 음수로 넣고, missions가 호출되면, 양수로 바꿈으로써 (미달성/달성-미확인/달성-확인) 을 구분하도록 한다.
				checkAchievement(user, 7, withObtain) # 업적 달성률에 대한 체크를 위해 재귀 호출
	
	if withObtain:
		for c in completedAchievements:
			obtainReward(user, c["reward"], c["amount"], True)
	return completedAchievements
	
def checkMission(user, params, trigger):
	def _checkDayily(user, userMissions, MISSIONS, statuses, params):
		# 각 트리거마다, 체크할 kind리스트를 만든다. (필터링하기 위해)
		if trigger==TRIGGER_GAME_RESULT:	vaildKinds = [0,1,2,3]
		elif trigger==TRIGGER_BUFF:			vaildKinds = [4,]
		elif trigger==TRIGGER_FEED:			vaildKinds = [5,]
		elif trigger==TRIGGER_LOGIN:		vaildKinds = [6,]
		else:								return
		
		# eval 에서 사용되는 변수들을 세팅한다. (game, combo, counts, fever 등등)
		index = 0
		for m in userMissions:
			if statuses[index]!=-1: # 이미 완료되었다면 패스
				index += 1
				continue
			mission = MISSIONS[m-1]
			kind = mission["kind"]
			if kind in vaildKinds:
				if kind==0: # 콤보
					game = params["game"]
					combo = params["combo"]
				elif kind==1: # 점수
					game = params["game"]
					score = params["score"]+params["bonusScore"]
				elif kind==2: # 피버
					game = params["game"]
					fever = params["fever"]
				elif kind==3: # 게임플레이횟수
					game = params["game"]
					user.mission_record.dplays[game] += 1
					counts = user.mission_record.dplays
				elif kind==4: # 호감도 버프 사용 횟수
					user.mission_record.dbuff += 1
					count = user.mission_record.dbuff
				elif kind==5: # 호감도 행동 횟수
					action = params["action"]
					user.mission_record.dfeeds[action] += 1
					count = user.mission_record.dfeeds[action]
				elif kind==6: # 출석
					count = user.daily_stamp
				if eval(mission["condition"]):
					statuses[index] = 1 # 완료(미확인)
					yield mission
			index += 1
	
	def _checkWeekly(user, userMissions, MISSIONS, statuses, params):
		# 각 트리거마다, 체크할 kind리스트를 만든다. (필터링하기 위해)
		if trigger==TRIGGER_GAME_RESULT:	vaildKinds = [0,1,2,3]
		elif trigger==TRIGGER_BUFF:			vaildKinds = [4,]
		elif trigger==TRIGGER_FEED:			vaildKinds = [5,]
		elif trigger==TRIGGER_LOGIN:		vaildKinds = [6,]
		else:								return
		
		# eval 에서 사용되는 변수들을 세팅한다. (game, combo, counts, fever 등등)
		index = 0
		for m in userMissions:
			if statuses[index]!=-1: # 이미 완료되었다면 패스
				index += 1
				continue
			mission = MISSIONS[m-1]
			kind = mission["kind"]
			if kind in vaildKinds:
				if kind==0: # 콤보
					game = params["game"]
					combo = params["combo"]
				elif kind==1: # 점수
					game = params["game"]
					score = params["score"]+params["bonusScore"]
				elif kind==2: # 피버
					game = params["game"]
					fever = params["fever"]
				elif kind==3: # 게임플레이횟수
					game = params["game"]
					user.mission_record.wplays[game] += 1
					counts = user.mission_record.wplays
				elif kind==4: # 호감도 버프 사용 횟수
					user.mission_record.wbuff += 1
					count = user.mission_record.wbuff
				elif kind==5: # 호감도 행동 횟수
					action = params["action"]
					user.mission_record.wfeeds[action] += 1
					count = user.mission_record.wfeeds[action]
				elif kind==6: # 출석
					count = user.daily_stamp
				if eval(mission["condition"]):
					statuses[index] = 1 # 완료(미확인)
					yield mission
			index += 1
	
	if trigger==TRIGGER_GAME_RESULT:
		dailyMissions = params["dailyMissions"]
		weeklyMissions = params["weeklyMissions"]
	else:
		todayMission = Mission.todayMission()
		dailyMissions = todayMission.daily_missions
		weeklyMissions = todayMission.weekly_missions
	
	completedMissions = []
	for m in _checkDayily(user, dailyMissions, getStorageData("daily-missions"), user.mission_record.daily_statuses, params):
		completedMissions.append({"name":m["name"], "reward":m["reward"], "amount":m["amount"]})
	for m in _checkWeekly(user, weeklyMissions, getStorageData("weekly-missions"), user.mission_record.weekly_statuses, params):
		completedMissions.append({"name":m["name"], "reward":m["reward"], "amount":m["amount"]})
	
	achievements = []
	for c in completedMissions:
		user.accrue_record.mission_completed += 1
		achievements += checkAchievement(user, 0)
	completedMissions += achievements
	
	# 통계에 보상항목들 추가, 여기엔 캐릭터가 들어오면 안됨(캐릭터는, 초대보상으로만 가능)
	REWARDS = [0,0,0]
	for c in completedMissions:
		REWARDS[c["reward"]] += c["amount"]
	if len(completedMissions)>0:
		defer(incrementStats, user.platform_id, REWARDS)
	return completedMissions

def send_purcase_to_kakao(userId, platform_id, uid, price, currency):
	host = "https://gameapi.kakao.com/payment_v1/payments.json"
	
	form_fields = {
		"client_id": str(KAKAO_CLIENT_ID),
		"secret_key": KAKAO_SECRET_KEY,
		"service_user_id": str(userId),
		"platform": ("apple" if platform_id==PLATFORM_IOS else "google"),
		"price": str(price),
		"currency": currency,
		"os": ("ios" if platform_id==PLATFORM_IOS else "android"),
		"country_iso":"KR",
		"buy_no":str(uid),
	}
	form_data = urllib.urlencode(form_fields)
	try:
		urlfetch.fetch(url=host,
		    payload=form_data,
		    method=urlfetch.POST,
		    headers={'Content-Type': 'application/x-www-form-urlencoded'}
		)
	except DeadlineExceededError as de:
		return True
	except Exception as e:
		return True
